package com.yang.serialport.tools;

import kotlin.Pair;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 创建时间: 2020/01/14 10:40 <br>
 * 作者: luyajun002 <br>
 * 描述:
 */
public class HTTPTools {

    private static OkHttpClient httpClient;

    static {
        // Create a trust manager that does not validate certificate chains
        final TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
        };

        // Install the all-trusting trust manager
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        if (sslContext != null) {
            try {
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        }
        // Create an ssl socket factory with our all-trusting manager
        assert sslContext != null;
        final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        HTTPTools.sslSocketFactory = sslSocketFactory;

        httpClient = new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0])
                .hostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                })
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(@NotNull HttpUrl httpUrl, @NotNull List<Cookie> list) {
                    }

                    @NotNull
                    @Override
                    public List<Cookie> loadForRequest(@NotNull HttpUrl httpUrl) {
                        return new ArrayList<>();
                    }
                })
                .followRedirects(false)
                .followSslRedirects(false)
                .connectTimeout(6, TimeUnit.SECONDS)
                .build();
    }

    public static javax.net.ssl.SSLSocketFactory sslSocketFactory;

    public static Response getResponse(String url, List<Map.Entry<String, String>> headers) {
        try {
            Request.Builder builder = new Request.Builder();
            builder.url(url);
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, String> entry : headers) {
                    builder.addHeader(entry.getKey(), entry.getValue());
                }
            }
            Request request = builder.build();
            return httpClient.newCall(request).execute();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Response getResponse(String url, List<Map.Entry<String, String>> headers, List<Map.Entry<String, String>> queryParams) {
        try {
            if (queryParams != null && queryParams.size() > 0) {
                if (!url.endsWith("?")) {
                    url += "?";
                }
                StringBuilder urlBuilder = new StringBuilder(url);
                for (Map.Entry<String, String> entry : queryParams) {
                    urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
                }
                url = urlBuilder.substring(0, urlBuilder.length());
            }
            Request.Builder builder = new Request.Builder();
            builder.url(url);
            for (Map.Entry<String, String> entry : headers) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
            Request request = builder.build();
            return httpClient.newCall(request).execute();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getResponseHeader(String url, List<Map.Entry<String, String>> headers, String headerKey) {
        try {
            Request.Builder builder = new Request.Builder();
            builder.url(url);
            if (headers != null && headers.size() > 0) {
                for (Map.Entry<String, String> entry : headers) {
                    builder.addHeader(entry.getKey(), entry.getValue());
                }
            }
            Request request = builder.build();
            Response response = httpClient.newCall(request).execute();
            getOrOrOutResponse(response, true);
            return response.headers().get(headerKey);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getResponseBody(String url, List<Map.Entry<String, String>> headers, boolean outPut) {
        try {
            Request.Builder builder = new Request.Builder();
            builder.url(url);
            if (headers != null && headers.size() > 0) {
                for (Map.Entry<String, String> entry : headers) {
                    builder.addHeader(entry.getKey(), entry.getValue());
                }
            }
            Request request = builder.build();
            Response response = httpClient.newCall(request).execute();
            return getOrOrOutResponse(response, outPut);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getResponseBody(String url, List<Map.Entry<String, String>> headers,
                                         List<Map.Entry<String, String>> queryParams, boolean outPut) {
        try {
            if (queryParams != null && queryParams.size() > 0) {
                if (!url.endsWith("?")) {
                    url += "?";
                }
                StringBuilder urlBuilder = new StringBuilder(url);
                for (Map.Entry<String, String> entry : queryParams) {
                    urlBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
                }
                url = urlBuilder.substring(0, urlBuilder.length());
            }
            Request.Builder builder = new Request.Builder();
            builder.url(url);
            for (Map.Entry<String, String> entry : headers) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
            Request request = builder.build();
            Response response = httpClient.newCall(request).execute();
            return getOrOrOutResponse(response, outPut);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Response postResponse(String url, List<Map.Entry<String, String>> headers, RequestBody formBody) {
        try {
            Request.Builder builder = new Request.Builder();
            builder.url(url).post(formBody);
            for (Map.Entry<String, String> entry : headers) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
            Request request = builder.build();
            return httpClient.newCall(request).execute();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String postResponseHeader(String url, List<Map.Entry<String, String>> headers, RequestBody formBody, String headerKey) {
        try {
            Request.Builder builder = new Request.Builder();
            builder.url(url).post(formBody);
            for (Map.Entry<String, String> entry : headers) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
            Request request = builder.build();
            Response response = httpClient.newCall(request).execute();
            getOrOrOutResponse(response, true);
            return response.headers().get(headerKey);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String postResponseBody(String url, List<Map.Entry<String, String>> headers, RequestBody formBody, boolean outPut) {
        try {
            Request.Builder builder = new Request.Builder();
            builder.url(url).post(formBody);
            for (Map.Entry<String, String> entry : headers) {
                builder.addHeader(entry.getKey(), entry.getValue());
            }
            Request request = builder.build();
            Response response = httpClient.newCall(request).execute();
            return getOrOrOutResponse(response, outPut);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getOrOrOutResponse(Response response, boolean output) {
        try {
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(Objects.requireNonNull(response.body()).byteStream(), StandardCharsets.UTF_8), 8 * 1024);
            StringBuilder entityStringBuilder = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                entityStringBuilder.append(line).append("\n");
            }
            if (output) {
                System.out.println("---------Response----------");
                System.out.println(response);
                System.out.println("---------------------------");
                System.out.println("------Response Header------");
                Headers headers = response.headers();
                headers.forEach(new Consumer<Pair<? extends String, ? extends String>>() {
                    @Override
                    public void accept(Pair<? extends String, ? extends String> pair) {
                        System.out.println(pair.component1() + "\t:\t" + pair.component2());
                    }
                });
                System.out.println("---------------------------");
                System.out.println("-------Response Body-------");
                System.out.println(entityStringBuilder);
                System.out.println("---------------------------");
            }
            return entityStringBuilder.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Map<String, String> getCookieMap(String... cookies) {
        Map<String, String> map = new HashMap<>();
        if (cookies != null && cookies.length != 0) {
            String[] cookieParts;
            for (String cookie : cookies) {
                cookieParts = cookie.split("=");
                map.put(cookieParts[0], strArrToStr(cookieParts, 1));
            }
        }
        return map;
    }

    public static String strArrToStr(String[] arr, int startIndex) {
        StringBuilder builder = new StringBuilder();
        for (int i = startIndex; i < arr.length; i++) {
            builder.append(arr[i]);
        }
        return builder.toString();
//        try {
//            return URLDecoder.decode(builder.toString(), "UTF-8");
//        } catch (UnsupportedEncodingException e) {
//            e.printStackTrace();
//            return builder.toString();
//        }
    }

    public static String getCookieStr(Map<String, String> cookieMap) {
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : cookieMap.entrySet()) {
            builder.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
        }
        return builder.toString();
    }

    public static String getAIpAddress() {
        return getAInt(255) + "." + getAInt(255) + "." + getAInt(255) + "." + getAInt(255);
    }

    public static int getAInt(int num) {
        return (int) (Math.random() * num);
    }

    public static OkHttpClient getHttpClient() {
        return httpClient;
    }
}
